package net.mem2b.codegen.jet.parser;

import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;

import net.mem2b.codegen.jet.EmitterException;
import net.mem2b.codegen.jet.ErrorCode;
import net.mem2b.codegen.jet.ExpressionElement;
import net.mem2b.codegen.jet.ScripletElement;
import net.mem2b.codegen.jet.TemplateElement;
import net.mem2b.codegen.jet.TextElement;

import org.antlr.runtime.ANTLRInputStream;
import org.antlr.runtime.CharStream;
import org.antlr.runtime.CommonTokenStream;
import org.antlr.runtime.RecognitionException;
import org.antlr.runtime.Token;
import org.antlr.runtime.tree.CommonTree;
import org.antlr.runtime.tree.CommonTreeAdaptor;
import org.antlr.runtime.tree.Tree;

public class TemplateParser {

	public List<TemplateElement> getElements() {
		return elements;
	}

	public String getClassName() {
		return className;
	}

	public String getPackageName() {
		return packageName;
	}

	public String getSkeleton() {
		return skeleton;
	}

	public String getImports() {
		return imports;
	}

	EmitterException faultReason = null;

	List<TemplateElement> elements = new ArrayList<TemplateElement>();

	private String className = "";
	private String packageName = "";
	private String skeleton = "";
	private String imports = "";

	public boolean parse(InputStream inputStream) {
		CharStream stream = null;
		try {
			stream = new ANTLRInputStream(inputStream);
			JetLexer lexer = new JetLexer(stream);
			CommonTokenStream tokens = new CommonTokenStream(lexer);
			JetParser parser = new JetParser(tokens);
			parser.setTreeAdaptor(new Adaptor());
			JetTree tree = (JetTree) parser.document().getTree();
			if (tree != null) {
				int count = tree.getChildCount();
				for (int i = 0; i < count; i++) {
					JetTree child = (JetTree) tree.getChild(i);
					if (child instanceof JetDirectiveTree) {
						JetDirectiveTree directive = (JetDirectiveTree) child;
						if (directive.getId() != null && directive.getId().equals("jet")) {
							packageName = directive.getAttribute("package");
							className = directive.getAttribute("class");
							skeleton = directive.getAttribute("skeleton");
							imports = directive.getAttribute("imports");
						}
					}
				}
				return true;
			} else {
				setFaultReason(new EmitterException(ErrorCode.TEMPLATE_PARSING_FAILED));
			}
		} catch (IOException e) {
			setFaultReason(new EmitterException(ErrorCode.TEMPLATE_READ_FAILED));
		} catch (RecognitionException e) {
			setFaultReason(new EmitterException(ErrorCode.TEMPLATE_PARSING_FAILED, e.getMessage()));
		}
		return false;
	}

	public EmitterException getFaultReason() {
		return faultReason;
	}

	public void setFaultReason(EmitterException faultReason) {
		this.faultReason = faultReason;
	}

	final class Adaptor extends CommonTreeAdaptor {

		private Adaptor() {
			super();
		}

		@Override
		public Object create(final Token payload) {
			if (payload == null) {
				return new JetTree(payload);
			}
			switch (payload.getType()) {
			case JetLexer.JET_DIRECTIVE:
				return new JetDirectiveTree(payload);
			case JetLexer.GENERIC_ID:
				return new JetGenericIdTree(payload);
			case JetLexer.ATTRIBUTES:
				return new JetAttributesTree(payload);
			case JetLexer.ATTRIBUTE:
				return new JetAttributeTree(payload);
			case JetLexer.JET_EXPRESSION:
				return new JetExpressionTree(payload);
			case JetLexer.JET_SCRIPTLET:
				return new JetScripletTree(payload);
			case JetLexer.PCDATA:
				return new JetPCDATATree(payload);
			default:
				return new JetTree(payload);
			}
		}
	}

	class JetTree extends CommonTree {
		public JetTree(Token payload) {
			super(payload);
		}

		public JetTree getChildByType(int type) {
			int count = getChildCount();
			for (int i = 0; i < count; i++) {
				JetTree tree = (JetTree) getChild(i);
				if (tree.getType() == type) {
					return tree;
				}
			}
			return null;
		}
	}

	class JetDirectiveTree extends JetTree {
		public JetDirectiveTree(Token payload) {
			super(payload);
		}

		public String getId() {
			Tree id = getChildByType(JetParser.GENERIC_ID);
			return id == null ? null : id.getText();
		}

		public String getAttribute(String name) {
			JetAttributesTree attrubutes = (JetAttributesTree) getChildByType(JetParser.ATTRIBUTES);
			if (attrubutes != null) {
				JetAttributeTree attrubute = attrubutes.getAttribute(name);
				if (attrubute != null) {
					return attrubute.getValue();
				}
			}
			return null;
		}
	}

	class JetGenericIdTree extends JetTree {
		public JetGenericIdTree(Token payload) {
			super(payload);
		}
	}

	class JetAttributesTree extends JetTree {
		public JetAttributesTree(Token payload) {
			super(payload);
		}

		public JetAttributeTree getAttribute(String name) {
			for (int i = 0; i < getChildCount(); i++) {
				JetAttributeTree attribute = (JetAttributeTree) getChild(i);
				if (attribute.getName().equals(name)) {
					return attribute;
				}
			}
			return null;
		}
	}

	class JetAttributeTree extends JetTree {
		public JetAttributeTree(Token payload) {
			super(payload);
		}

		public String getName() {
			return getChild(0).getText();
		}

		public String getValue() {
			return getChild(1).getText();
		}
	}

	class JetExpressionTree extends JetTree {
		public JetExpressionTree(Token payload) {
			super(payload);
			elements.add(new ExpressionElement(payload.getText()));
		}
	}

	class JetScripletTree extends JetTree {
		public JetScripletTree(Token payload) {
			super(payload);
			elements.add(new ScripletElement(payload.getText()));
		}
	}

	class JetPCDATATree extends JetTree {
		public JetPCDATATree(Token payload) {
			super(payload);
			elements.add(new TextElement(payload.getText()));
		}
	}
}
